package com.mf.framework.component.excel;

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.*;

import java.beans.PropertyDescriptor;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.*;

/**
 * Excel导出工具类(.xlsx)
 * User: zhaoming
 * DateTime: 2017-3-18
 */
public class ExcelExportXSSFUtils<T> extends ExcelExportBase{

    //工作薄
    private XSSFWorkbook wb = null;
    //工作表
    private XSSFSheet sheet = null;
    //映射位置关系
    private Map<String, Integer>  positionMappingMap = new HashMap<String, Integer>();
    //默认行高
    public static short DEFAULT_ROW_HEIGHT = 600;


    /**
     * 构造函数
     * @param wb 工作薄
     * @param sheet 工作表
     */
    public ExcelExportXSSFUtils(XSSFWorkbook wb, XSSFSheet sheet) {
        this.wb = wb;
        this.sheet = sheet;
    }

    /**
     * 创建标题
     * @param title 标题
     * @param mergeColumn 合并列数
     */
    public void createTile(String title, int mergeColumn) {
        this.createTile(title, mergeColumn, this.getTitleStyle(wb));
    }

    /**
     * 创建标题
     * @param title 标题
     * @param mergeColumn 合并列数
     * @param cellStyle 单元格样式
     */
    public void createTile(String title, int mergeColumn, CellStyle cellStyle){
        //创建行
        XSSFRow row = sheet.createRow(0);
        row.setHeight(DEFAULT_ROW_HEIGHT);
        //创建单元格
        XSSFCell cell = row.createCell(0);
        cell.setCellType(CellType.STRING);
        cell.setCellValue(new XSSFRichTextString(title));
        cell.setCellStyle(cellStyle);
        //指定合并区域
        sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, mergeColumn));
        this.setBorder(1, new CellRangeAddress(0, 0, 0, mergeColumn), sheet);
    }

    /**
     * 创建副标题
     * @param subTile 要显示的内容
     * @param mergeColumn 合并列数
     */
    public void createSubTitle(String subTile, int mergeColumn) {
        this.createSubTitle(subTile, mergeColumn, this.getSubTitleStyle(wb));
    }

    /**
     * 创建副标题
     * @param subTile 要显示的内容
     * @param mergeColumn 合并列数
     * @param cellStyle 单元格样式
     */
    public void createSubTitle(String subTile, int mergeColumn, CellStyle cellStyle) {
        //创建行
        XSSFRow row = sheet.createRow(1);
        row.setHeight(DEFAULT_ROW_HEIGHT);
        //创建单元格
        XSSFCell cell = row.createCell(0);
        cell.setCellType(CellType.STRING);
        cell.setCellValue(new XSSFRichTextString(subTile));
        cell.setCellStyle(cellStyle);
        // 指定合并区域
        sheet.addMergedRegion(new CellRangeAddress(1, 1, 0, mergeColumn));
        this.setBorder(1, new CellRangeAddress(1, 1, 0, mergeColumn), sheet);
    }

    /**
     * 创建虚拟列表头
     * @param virtualColumns 虚拟列集合
     * @param rowIndex 行号
     */
    public void createVirtualHeader(LinkedList<VirtualColumn> virtualColumns, int rowIndex){
        this.createVirtualHeader(virtualColumns, rowIndex, this.getHeaderStyle(wb));
    }

    /**
     * 创建虚拟列表头
     * @param virtualColumns 虚拟列集合
     * @param rowIndex 行号
     * @param cellStyle 单元格样式
     */
    public void createVirtualHeader(LinkedList<VirtualColumn> virtualColumns, int rowIndex, CellStyle cellStyle){
        //创建行
        XSSFRow row = sheet.createRow(rowIndex);
        row.setHeight(DEFAULT_ROW_HEIGHT);

        for (VirtualColumn virtualColumn : virtualColumns) {
            //创建单元格
            XSSFCell cell = row.createCell(virtualColumn.getFirstCol());
            cell.setCellType(CellType.STRING);
            cell.setCellValue(new XSSFRichTextString(virtualColumn.getColumnName()));
            cell.setCellStyle(cellStyle);
            // 指定合并区域
            if(virtualColumn.getFirstCol() < virtualColumn.getLastCol()){
                CellRangeAddress cellRangeAddress = new CellRangeAddress(rowIndex, rowIndex, virtualColumn.getFirstCol(), virtualColumn.getLastCol());
                sheet.addMergedRegion(cellRangeAddress);
                this.setBorder(1, cellRangeAddress, sheet);
            }
        }
    }

    /**
     * 创建正常列表
     * @param normalColumns 列表集合
     * @param rowIndex 行号
     */
    public void createHeader(LinkedList<NormalColumn> normalColumns, int rowIndex){
        this.createHeader(normalColumns, rowIndex, this.getHeaderStyle(wb));
    }

    /**
     * 创建正常列表
     * @param normalColumns 列表集合
     * @param rowIndex 行号
     * @param cellStyle 单元格样式
     */
    public void createHeader(LinkedList<NormalColumn> normalColumns, int rowIndex, CellStyle cellStyle) {
        // 创建行
        XSSFRow row = sheet.createRow(rowIndex);
        row.setHeight(DEFAULT_ROW_HEIGHT);

        int i = 0;
        for (NormalColumn normalColumn : normalColumns) {
            //创建单元格
            XSSFCell cell = row.createCell(i);
            cell.setCellType(CellType.STRING);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(new XSSFRichTextString(normalColumn.getColumnName()));
            //构建映射位置关系
            if(StringUtils.isNotBlank(normalColumn.getColumnCode())){
                positionMappingMap.put(normalColumn.getColumnCode(), i);
            }
            //设置列宽
            sheet.setColumnWidth(i, normalColumn.getColumnWidth());
            i++;
        }
    }

    /**
     * 创建内容
     * @param ts 业务数据
     * @param rowIndex 行号
     */
    public void createCells(List<T> ts, Integer rowIndex){
        int index = 0;
        for(T t : ts){
            //创建行
            XSSFRow row = sheet.createRow(rowIndex + index);
            row.setHeight(DEFAULT_ROW_HEIGHT);

            Class clazz = t.getClass();
            //获得属性
            Field[] fields = clazz.getDeclaredFields();

            for(Field field : fields){
                if(positionMappingMap.containsKey(field.getName())){
                    try{
                        XSSFCell cell = row.createCell(positionMappingMap.get(field.getName()));
                        PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz);
                        Method method = pd.getReadMethod();
                        Object object = method.invoke(t);
                        if(field.getType().equals(String.class)){
                            cell.setCellType(CellType.STRING);
                            cell.setCellValue(new XSSFRichTextString(object.toString()));
                            cell.setCellStyle(this.getStringCellStyle(wb));
                        }else if(field.getType().equals(Integer.class)){
                            cell.setCellType(CellType.NUMERIC);
                            cell.setCellValue((Integer)object);
                            cell.setCellStyle(this.getIntegerCellStyle(wb));
                        }else if(field.getType().equals(Short.class)){
                            cell.setCellType(CellType.NUMERIC);
                            cell.setCellValue((Short)object);
                            cell.setCellStyle(this.getIntegerCellStyle(wb));
                        }else if(field.getType().equals(Long.class)){
                            cell.setCellType(CellType.NUMERIC);
                            cell.setCellValue((Long)object);
                            cell.setCellStyle(this.getIntegerCellStyle(wb));
                        }else if(field.getType().equals(Float.class)){
                            cell.setCellType(CellType.NUMERIC);
                            cell.setCellValue((Float)object);
                            cell.setCellStyle(this.getFloatCellStyle(wb));
                        }else if(field.getType().equals(Double.class)){
                            cell.setCellType(CellType.NUMERIC);
                            cell.setCellValue((Double)object);
                            cell.setCellStyle(this.getFloatCellStyle(wb));
                        }else if(field.getType().equals(BigDecimal.class)){
                            cell.setCellType(CellType.NUMERIC);
                            cell.setCellValue((Double)object);
                            cell.setCellStyle(this.getFloatCellStyle(wb));
                        }else if(field.getType().equals(Date.class)){
                            cell.setCellType(CellType.NUMERIC);
                            cell.setCellValue((Date)object);
                            cell.setCellStyle(this.getDateCellStyle(wb));
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                        throw new RuntimeException(e.getMessage(), e);
                    }
                }
            }
            index++;
        }
    }

    /**
     * 创建内容单元格(填充字符型)
     * @param row   XSSFRow 行对象
     * @param col   short型的列索引
     * @param val   列值
     */
    public void createCell(XSSFRow row, int col, String val) {
        this.createCell(row, col, val, this.getStringCellStyle(wb));
    }

    /**
     * 创建内容单元格(填充字符型)
     * @param row   XSSFRow
     * @param col   short型的列索引
     * @param val   列值
     * @param cellStyle 对齐样式
     */
    public void createCell(XSSFRow row, int col,  String val, CellStyle cellStyle) {
        XSSFCell cell = row.createCell(col);
        cell.setCellType(CellType.STRING);
        cell.setCellValue(new XSSFRichTextString(val));
        cell.setCellStyle(cellStyle);
    }

    /**
     * 创建内容单元格(填充BigDecimal型)
     * @param row   XSSFRow 行对象
     * @param col   short型的列索引
     * @param val   列值
     */
    public void createCell(XSSFRow row, int col, BigDecimal val) {
        this.createCell(row, col, val, this.getFloatCellStyle(wb));
    }

    /**
     * 创建内容单元格(填充BigDecimal型)
     * @param row   XSSFRow 行对象
     * @param col   short型的列索引
     * @param val   列值
     * @param cellStyle 对齐样式
     */
    public void createCell(XSSFRow row, int col, BigDecimal val, CellStyle cellStyle) {
        XSSFCell cell = row.createCell(col);
        cell.setCellStyle(cellStyle);
        cell.setCellType(CellType.NUMERIC);
        cell.setCellValue(val.doubleValue());
    }

    /**
     * 创建内容单元格(填充double型)
     * @param row   XSSFRow 行对象
     * @param col   short型的列索引
     * @param val   列值
     */
    public void createCell(XSSFRow row, int col, double val) {
        this.createCell(row, col, val, this.getFloatCellStyle(wb));
    }

    /**
     * 创建内容单元格(填充double型)
     * @param row   XSSFRow 行对象
     * @param col   short型的列索引
     * @param val   列值
     * @param cellStyle 对齐样式
     */
    public void createCell(XSSFRow row, int col, double val, CellStyle cellStyle) {
        XSSFCell cell = row.createCell(col);
        cell.setCellStyle(cellStyle);
        cell.setCellType(CellType.NUMERIC);
        cell.setCellValue(val);
    }

    /**
     * 创建内容单元格(填充float型)
     * @param row   XSSFRow 行对象
     * @param col   short型的列索引
     * @param val   列值
     */
    public void createCell(XSSFRow row, int col, float val) {
        this.createCell(row, col, val, this.getFloatCellStyle(wb));
    }

    /**
     * 创建内容单元格(填充float型)
     * @param row   XSSFRow 行对象
     * @param col   short型的列索引
     * @param val   列值
     * @param cellStyle 对齐样式
     */
    public void createCell(XSSFRow row, int col, float val, CellStyle cellStyle) {
        XSSFCell cell = row.createCell(col);
        cell.setCellStyle(cellStyle);
        cell.setCellType(CellType.NUMERIC);
        cell.setCellValue(val);
    }

    /**
     * 创建内容单元格(填充long型)
     * @param row   XSSFRow 行对象
     * @param col   short型的列索引
     * @param val   列值
     */
    public void createCell(XSSFRow row, int col, long val) {
        this.createCell(row, col, val, this.getIntegerCellStyle(wb));
    }

    /**
     * 创建内容单元格(填充long型)
     * @param row   XSSFRow 行对象
     * @param col   short型的列索引
     * @param val   列值
     * @param cellStyle 对齐样式
     */
    public void createCell(XSSFRow row, int col, long val, CellStyle cellStyle) {
        XSSFCell cell = row.createCell(col);
        if (val == 0) {
            cell.setCellType(CellType.STRING);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(new XSSFRichTextString(""));
        } else {
            cell.setCellType(CellType.NUMERIC);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(val);
        }
    }

    /**
     * 创建内容单元格(填充int型)
     * @param row   XSSFRow 行对象
     * @param col   short型的列索引
     * @param val   列值
     */
    public void createCell(XSSFRow row, int col, int val) {
        this.createCell(row, col, val, this.getIntegerCellStyle(wb));
    }

    /**
     * 创建内容单元格(填充int型)
     * @param row   XSSFRow 行对象
     * @param col   short型的列索引
     * @param val   列值
     * @param cellStyle 对齐样式
     */
    public void createCell(XSSFRow row, int col, int val, CellStyle cellStyle) {
        XSSFCell cell = row.createCell(col);
        cell.setCellStyle(cellStyle);
        cell.setCellType(CellType.NUMERIC);
        cell.setCellValue(val);
    }

    /**
     * 创建内容单元格(填充short型)
     * @param row   XSSFRow 行对象
     * @param col   short型的列索引
     * @param val   列值
     */
    public void createCell(XSSFRow row, int col, short val) {
        this.createCell(row, col, val, this.getIntegerCellStyle(wb));
    }

    /**
     * 创建内容单元格(填充short型)
     * @param row   XSSFRow 行对象
     * @param col   short型的列索引
     * @param val   列值
     * @param cellStyle 对齐样式
     */
    public void createCell(XSSFRow row, int col, short val, CellStyle cellStyle) {
        XSSFCell cell = row.createCell(col);
        cell.setCellStyle(cellStyle);
        cell.setCellType(CellType.NUMERIC);
        cell.setCellValue(val);
    }

    /**
     * 创建内容单元格(填充Date型)
     * @param row   XSSFRow 行对象
     * @param col   short型的列索引
     * @param val   列值
     */
    public void createCell(XSSFRow row, int col, Date val) {
        this.createCell(row, col, val, this.getDateCellStyle(wb));
    }

    /**
     * 创建内容单元格(填充Date型)
     * @param row   XSSFRow 行对象
     * @param col   short型的列索引
     * @param val   列值
     * @param cellStyle 对齐样式
     */
    public void createCell(XSSFRow row, int col, Date val, CellStyle cellStyle) {
        XSSFCell cell = row.createCell(col);
        cell.setCellStyle(cellStyle);
        cell.setCellType(CellType.NUMERIC);
        cell.setCellValue(val);
    }

    /**
     * 创建尾行
     * @param cellValue 单元格值
     * @param mergeColumn 合并的列数
     */
    public void createFooter(String cellValue, int mergeColumn){
        this.createFooter(cellValue, mergeColumn, this.getFooterStyle(wb));
    }

    /**
     * 创建尾行
     * @param cellValue 单元格值
     * @param mergeColumn 合并的列数
     * @param cellStyle 单元格样式
     */
    public void createFooter(String cellValue, int mergeColumn, CellStyle cellStyle) {
        //创建行
        int lastRow = sheet.getLastRowNum() + 1;
        XSSFRow row = sheet.createRow(lastRow);
        row.setHeight(DEFAULT_ROW_HEIGHT);
        //创建单元格
        XSSFCell cell = row.createCell(0);
        cell.setCellType(CellType.STRING);
        cell.setCellValue(new XSSFRichTextString(cellValue));
        cell.setCellStyle(cellStyle);
        // 指定合并区域
        sheet.addMergedRegion(new CellRangeAddress(lastRow, lastRow, 0, mergeColumn));
        this.setBorder(1, new CellRangeAddress(lastRow, lastRow, 0, mergeColumn), sheet);
    }

    /**
     * 输入EXCEL文件
     * @param fileName 文件名
     */
    public void outputExcel(String fileName) {
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(new File(fileName));
            wb.write(fos);
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}